/**
 * @(#)Manager.java		
 * 
 * History:
 * 1.0	17 April 2012
 * 		Added behaviour to create rowers: CreateRower
 * 		Added behaviour to register rower: RegisterRower
 * 		Added behaviour to wait the registration reply from the Registration Desk: AwaitingRegistrationReply
 * 2.0  1 May 2012
 * 		Added behaviour to subscribe to the Referee service
 * 2.1  2 May 2012
 * 		Moved from upm.pb4agent.team1 to upm.pb4agent.team1.team
 * 		Extend AgBase1 instead of Agent
 * 		Transform CreateRower from class into a procedure
 * 		Transform RegisterRower from class into a procedure
 * 		Rename AwaitingRegistrationReply to ReceiveRegistrationReply
 * 2.2  7 May 2012
 * 		Add protocol to messages that involve the use of the ontology
 * 3.0	12 May 2012
 * 		Use enumeration for dock number (A, B, C, D) 
 * 		Send message to rowers to cancel subscription when registration is failed
 * 4.0	18 May 2012
 * 		Changed ReceiveStartRaceSignal into ReceiveRaceStartedSignal
 * 		Added behaviour to receive race finished signal: ReceiveRaceFinishedSignal
 * 		Removed ReceivedStartRaceSignal, ReceiveRaceFinishedSignal, and ReceiveBoatInformation since they are not used at all.
 */
package upm.pb4agent.team1.team;

import jade.content.lang.Codec.CodecException;
import jade.content.onto.OntologyException;
import jade.content.onto.basic.Action;
import jade.core.AID;
import jade.core.Agent;
import jade.core.behaviours.SimpleBehaviour;
import jade.domain.FIPAException;
import jade.lang.acl.ACLMessage;
import jade.lang.acl.MessageTemplate;
import jade.wrapper.AgentContainer;
import jade.wrapper.AgentController;
import jade.wrapper.ControllerException;
import jade.wrapper.StaleProxyException;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

import upm.pb4agent.ontology.Dock;
import upm.pb4agent.ontology.DockDesignation;
import upm.pb4agent.ontology.Registration;
import upm.pb4agent.ontology.RegistrationForm;
import upm.pb4agent.ontology.TeamID;
import upm.pb4agent.team1.AgBase1;
import upm.pb4agent.team1.ontology.CancelSubscription;
import upm.pb4agent.team1.ontology.InformRowersPosition;
import upm.pb4agent.team1.team.Constant.DockNumber;
import upm.pb4agent.team1.util.AgentUtil;
import upm.pb4agent.team1.util.LocalRower;

/**
 * This class handles the manager functions: (1) Create 4 rowers and assigned
 * them to different docks and also create 1 watcher. (2) Register the rowers by
 * sending Registration Form to the Registration Desk. (3) Subscribe to the
 * Referee to be notified if the race is started.
 * 
 * @version 4.0
 * @author Bob Nicolaus
 * 
 */
public class Manager extends AgBase1 {

	private static final long serialVersionUID = 4283953927888777831L;

	private static final String ROWER_1_1_NAME = "Rower11";
	private static final String ROWER_1_2_NAME = "Rower12";
	private static final String ROWER_1_3_NAME = "Rower13";
	private static final String ROWER_1_4_NAME = "Rower14";
	private static final String TEAM_1_ID = "1";
	private List<LocalRower> rowerList = new ArrayList<LocalRower>();

	@Override
	protected void setup() {

		System.out.println(getLocalName() + ": Enters the system");

		createRower();
		registerRower();

		// Add behaviours
		addBehaviour(new ReceiveRegistrationReply(this));
	}

	/**
	 * Creates 4 rowers
	 */
	private void createRower() {
		LocalRower rower = null;

		float strength = 0;
		float resistance = 0;
		float agility = 0;
		float rowSeconds = 0;
		float frontOarYCoef = 0;
		float backOarYCoef = 0;
		float timeChangeSeat = 0;

		int circuitLength = 100;

		String firstSeatID;

		// Create 4 rower agents
		try {
			strength = Float.valueOf(AgentUtil.getProperty("rower1_strength"));
			resistance = Float.valueOf(AgentUtil
					.getProperty("rower1_resistance"));
			agility = Float.valueOf(AgentUtil.getProperty("rower1_agility"));
			LocalRower rower1 = new LocalRower(ROWER_1_1_NAME, strength,
					resistance, agility);
			rowerList.add(rower1);

			strength = Float.valueOf(AgentUtil.getProperty("rower2_strength"));
			resistance = Float.valueOf(AgentUtil
					.getProperty("rower2_resistance"));
			agility = Float.valueOf(AgentUtil.getProperty("rower2_agility"));
			LocalRower rower2 = new LocalRower(ROWER_1_2_NAME, strength,
					resistance, agility);
			rowerList.add(rower2);

			strength = Float.valueOf(AgentUtil.getProperty("rower3_strength"));
			resistance = Float.valueOf(AgentUtil
					.getProperty("rower3_resistance"));
			agility = Float.valueOf(AgentUtil.getProperty("rower3_agility"));
			LocalRower rower3 = new LocalRower(ROWER_1_3_NAME, strength,
					resistance, agility);
			rowerList.add(rower3);

			strength = Float.valueOf(AgentUtil.getProperty("rower4_strength"));
			resistance = Float.valueOf(AgentUtil
					.getProperty("rower4_resistance"));
			agility = Float.valueOf(AgentUtil.getProperty("rower4_agility"));
			LocalRower rower4 = new LocalRower(ROWER_1_4_NAME, strength,
					resistance, agility);
			rowerList.add(rower4);

			// Sort the list and assign the dock
			Collections.sort(rowerList);
			rowerList.get(0).setDock(DockNumber.A);
			rowerList.get(1).setDock(DockNumber.B);
			rowerList.get(2).setDock(DockNumber.C);
			rowerList.get(3).setDock(DockNumber.D);

			rowSeconds = 1000 * Float.valueOf(AgentUtil
					.getProperty("row_seconds"));
			frontOarYCoef = Float.valueOf(AgentUtil
					.getProperty("front_oar_y_coef"));
			backOarYCoef = Float.valueOf(AgentUtil
					.getProperty("back_oar_y_coef"));
			timeChangeSeat = Float.valueOf(AgentUtil
					.getProperty("time_change_seat"));

			if (frontOarYCoef >= backOarYCoef) {
				firstSeatID = "2";
			} else {
				firstSeatID = "5";
			}

			circuitLength = Integer.valueOf(AgentUtil
					.getProperty("circuit_length"));

			// Create the agents
			Iterator<LocalRower> iterator = rowerList.iterator();
			while (iterator.hasNext()) {
				rower = iterator.next();

				createNewAgent(
						rower.getName(),
						Rower.class.getCanonicalName(),
						new Object[] { rower.getStrength(),
								rower.getResistance(), rower.getAgility(),
								rower.getDock(), rowSeconds, firstSeatID,
								timeChangeSeat, circuitLength });
				rower.setAgentID(new AID(rower.getName(), AID.ISLOCALNAME));

				// Sleep to give a chance for the agent
				Thread.sleep(200);
			}
		} catch (StaleProxyException e) {
			e.printStackTrace();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

	/**
	 * Registers rowers to the Registration Desk.
	 */
	private void registerRower() {

		AID registrationDeskAID = null;
		LocalRower rower = null;
		Dock dock = null;
		DockDesignation dockDesignation = null;

		TeamID teamID = new TeamID();
		teamID.setValue(TEAM_1_ID);

		// Construct registration
		Registration registration = new Registration();

		RegistrationForm registrationForm = new RegistrationForm();
		registrationForm.setTeamID(teamID);

		Iterator<LocalRower> iterator = rowerList.iterator();
		while (iterator.hasNext()) {
			rower = iterator.next();

			dock = new Dock();
			dock.setName(rower.getDock().toString());
			dockDesignation = new DockDesignation();
			dockDesignation.setDock(dock);
			dockDesignation.setRowerID(rower.getAgentID());
			registrationForm.addDockDesignation(dockDesignation);
		}

		registration.setRegistrationForm(registrationForm);

		// Send request message to Registration Desk
		try {

			registrationDeskAID = AgentUtil.searchAgentInDF(this,
					Constant.REGISTRATION_DESK_DF_TYPE);

			Action action = new Action(registrationDeskAID, registration);

			sendMessage(ACLMessage.REQUEST, registrationDeskAID, action,
					Constant.REGISTRATION_PROTOCOL);
		} catch (CodecException ce) {
			ce.printStackTrace();
		} catch (OntologyException oe) {
			oe.printStackTrace();
		} catch (FIPAException e) {
			e.printStackTrace();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

	/**
	 * This class handles the reply from the Registration Desk.
	 * 
	 * @author Morteza Nokhodian
	 * 
	 */
	class ReceiveRegistrationReply extends SimpleBehaviour {

		private static final long serialVersionUID = -7842952916094279836L;

		private boolean isDone = false;

		public ReceiveRegistrationReply(Agent agent) {
			super(agent);
		}

		@Override
		public void action() {
			ACLMessage registrationReplyMessage = receive(MessageTemplate.and(
					standardTemplate, MessageTemplate
							.MatchProtocol(Constant.REGISTRATION_PROTOCOL)));

			if (registrationReplyMessage != null) {

				// Registration is valid
				if (registrationReplyMessage.getPerformative() == ACLMessage.INFORM) {

					try {

						// Inform rower's position to every rower
						jade.util.leap.List rowerAIDList = new jade.util.leap.ArrayList();
						for (int i = 0; i < rowerList.size(); i++) {
							rowerAIDList.add(rowerList.get(i).getAgentID());
						}
						for (int i = 0; i < rowerList.size(); i++) {
							InformRowersPosition informRowersPosition = new InformRowersPosition();
							informRowersPosition.setRowerAIDList(rowerAIDList);
							Action action = new Action(rowerList.get(i)
									.getAgentID(), informRowersPosition);
							sendInternalMessage(ACLMessage.INFORM, rowerList
									.get(i).getAgentID(), action,
									Constant.INFORM_ROWERS_POSITION_PROTOCOL);
						}
					} catch (CodecException ce) {
						ce.printStackTrace();
					} catch (OntologyException oe) {
						oe.printStackTrace();
					}
				} else if (registrationReplyMessage.getPerformative() == ACLMessage.REFUSE
						|| registrationReplyMessage.getPerformative() == ACLMessage.NOT_UNDERSTOOD) {

					// Message is wrong
					try {

						// Send message to rowers to cancel subscription of
						// referee service
						Iterator<LocalRower> iterator = rowerList.iterator();
						AID rowerAID = null;
						while (iterator.hasNext()) {
							rowerAID = iterator.next().getAgentID();

							Action action = new Action(rowerAID,
									new CancelSubscription());
							sendInternalMessage(ACLMessage.INFORM, rowerAID,
									action,
									Constant.CANCEL_SUBSCRIPTION_PROTOCOL);
						}

						// Destroy the rowers
						destroyRower();
					} catch (ControllerException e) {
						e.printStackTrace();
					} catch (CodecException e) {
						e.printStackTrace();
					} catch (OntologyException e) {
						e.printStackTrace();
					}
				}

				isDone = true;
			} else {

				block();
			}
		}

		@Override
		public boolean done() {
			return isDone == true;
		}

		/**
		 * Destroys the 4 rowers.
		 * 
		 * @throws ControllerException
		 */
		private void destroyRower() throws ControllerException {

			try {
				Thread.sleep(5000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}

			AgentContainer agentContainer = getContainerController();

			AgentController agentController = agentContainer
					.getAgent(ROWER_1_1_NAME);
			agentController.kill();

			agentController = agentContainer.getAgent(ROWER_1_2_NAME);
			agentController.kill();

			agentController = agentContainer.getAgent(ROWER_1_3_NAME);
			agentController.kill();

			agentController = agentContainer.getAgent(ROWER_1_4_NAME);
			agentController.kill();
		}
	}
}